home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Turnbull China Bikeride
/
Turnbull China Bikeride - Disc 1.iso
/
ARGONET
/
PD
/
SOUND
/
REPLAYER.SPK
/
c
/
driver
< prev
next >
Wrap
Text File
|
1998-08-24
|
11KB
|
348 lines
/* driver.c
Replayer -- audio player
Copyright (c) 1997/8 Mark Seaborn <mseaborn@argonet.co.uk>
Contains functions for dealing with Replay sound drivers. Allows the
drivers to be used for tasks outside of playing Replay files.
*/
#include <stdlib.h>
#include <string.h>
#include "swis.h"
#include "OS:os.h"
#include "OS:osbyte.h"
#include "OS:osfile.h"
#include "OS:osmodule.h"
#ifdef MemCheck_MEMCHECK
#include "MemCheck:MemCheck.h"
#endif
#include "replaydriver.h"
/* Declarations */
#define DYNAMIC_AREA_NAME "Replay sound data"
#define SOUND_CHECK_TIME 100 /* centiseconds */
/* Loading and freeing drivers. */
/* Load the Replay sound driver whose filename is given, and return this
driver. Returns 0 for failure (driver not found, or out of memory).
You can pass the full pathname of the driver or just the leafname. */
replaydriver_driver *replaydriver_load(const char *filename)
{
replaydriver_driver *driver = 0;
int exists, size;
char *copy = 0;
/* Find size of file. */
xosfile_read_stamped(filename, &exists, 0, 0, &size, 0, 0);
if(~exists & fileswitch_IS_FILE) {
/* If the file wasn't found, prepend a directory name. */
int len = strlen(filename);
copy = malloc(strlen(replaydriver_DRIVER_PREFIX) + len + 1);
if(!copy) goto fail;
strcpy(copy, replaydriver_DRIVER_PREFIX);
memcpy(copy + strlen(replaydriver_DRIVER_PREFIX), filename, len + 1);
filename = copy;
/* Try getting the info again. */
if(xosfile_read_stamped(filename, 0, 0, 0, &size, 0, 0)) goto fail;
}
/* Allocate memory from RMA. */
if(xosmodule_alloc(size, (void **) &driver) || !driver) goto fail;
/* Load code. The following line should be StrongARM compatible. It is
the same as this OSLib SWI call, but with bit 31 (sync cache) set:
osfile_load_stamped(code_filename, (byte *) code, 0, 0, 0, 0); */
if(_swix(OS_File, _IN(0)|_IN(1)|_IN(2)|_IN(3), 12,
filename, driver, 1u<<31)) goto fail;
/* Register the driver's block with MemCheck, as we need to access it
directly later to set and read various things. */
#ifdef MemCheck_MEMCHECK
MemCheck_RegisterMiscBlock(driver, size);
#endif
free(copy);
return driver;
fail:
free(copy);
if(driver) xosmodule_free(driver);
return 0;
}
/* Frees the space used by the Replay sound driver. */
void replaydriver_destroy(replaydriver_driver *driver)
{
if(!driver) return;
xosmodule_free(driver);
#ifdef MemCheck_MEMCHECK
MemCheck_UnRegisterMiscBlock(driver);
#endif
}
/* Creating and freeing control blocks. */
/* Allocate a control block for the driver. Returns 0 for failure. All
fields are zeroed before returning, except for the quality field (set to
maximum quality). */
replaydriver_control *replaydriver_control_create(void)
{
replaydriver_control *control;
/* Allocate a block in the RMA. */
if(xosmodule_alloc(sizeof(replaydriver_control), (void **) &control)
|| !control) return 0;
#ifdef MemCheck_MEMCHECK
MemCheck_RegisterMiscBlock(control, sizeof(replaydriver_control));
#endif
/* Zero all fields. */
memset(control, 0, sizeof(replaydriver_control));
/* Set other defaults. */
control->data.quality = 4;
return control;
}
/* Free a previously allocated control block. */
void replaydriver_control_destroy(replaydriver_control *control)
{
if(!control) return;
/* Free and deregister the block. */
xosmodule_free(control);
#ifdef MemCheck_MEMCHECK
MemCheck_UnRegisterMiscBlock(control);
#endif
}
/* Allocating and freeing sound buffers and temporary space. */
/* Allocates two sound buffers, when given the size each buffer must be.
Fills the driver given with the locations of the buffers. Returns 0 for
failure. */
replaydriver_buffer *replaydriver_buffer_create(int single_size,
replaydriver_driver *driver)
{
replaydriver_buffer *buf = 0;
size_t total_size;
int os_version, i;
void *buffers[2];
if(!driver || single_size <= 0) return 0;
/* Allocate info block. */
buf = malloc(sizeof(replaydriver_buffer));
if(!buf) return 0;
buf->single_size = single_size;
buf->temp_size = 0;
/* Allocate space for the buffers. */
total_size = (single_size + 4) * 2;
if(xos_byte(129, 0, 0xFF, &os_version, 0)) goto fail;
if(os_version >= 0xA5) {
/* Running under RISC OS 3.5+, so use a dynamic area. */
if(xosdynamicarea_create(-1, total_size, (byte *) -1, 1<<7,
total_size * 4 /* Max size */, 0, 0, DYNAMIC_AREA_NAME,
&buf->dynarea, (byte **) &buf->buffer, 0))
goto use_rma_buffer;
}
else {
/* Dynamic areas not available, so allocate from module area. */
use_rma_buffer:
if(xosmodule_alloc(total_size, &buf->buffer) || !buf->buffer) goto fail;
buf->dynarea = -1;
}
#ifdef MemCheck_MEMCHECK
MemCheck_RegisterMiscBlock(buf->buffer, total_size);
#endif
/* Initialise buffers and attach to the driver. */
buffers[0] = (char *) buf->buffer;
buffers[1] = (char *) buf->buffer + single_size + 4;
for(i=0; i<2; ++i) {
replaydriver_set_buffer(driver, i, buffers[i]);
((int *) buffers[i])[1] = 1; /* Set state to unfilled. */
*(int *) ((char *) buffers[i] + single_size) =
replaydriver_BUFFER_CHECK_WORD;
}
/* Return successfully. */
return buf;
/* Return unsuccessfully. */
fail:
free(buf);
return 0;
}
/* Frees a previously-allocated set of sound buffers. */
void replaydriver_buffer_destroy(replaydriver_buffer *buf)
{
if(!buf) return;
/* Free the buffer itself. */
if(buf->dynarea != -1) {
/* Free the dynamic area. */
xosdynamicarea_delete(buf->dynarea);
}
else {
/* Free the module area block. */
xosmodule_free(buf->buffer);
}
#ifdef MemCheck_MEMCHECK
MemCheck_UnRegisterMiscBlock(buf->buffer);
#endif
/* Free the buffer info block. */
free(buf);
}
/* Allocates a temporary data block. Takes an already-allocated buffer
object, so that the new block can be tacked onto the end of an existing
dynamic area. Only one temporary block can be used for each buffer at a
time. Returns 0 for failure. */
void *replaydriver_temp_alloc(replaydriver_buffer *buf, size_t size)
{
void *block;
if(buf && buf->dynarea != -1) {
/* If the sound buffers use a dynamic area, extend the area. If an
error occurs, fall back to using the module area. */
int page_size, old_size;
if(xos_read_dynamic_area(buf->dynarea, (byte **) &block, &old_size, 0))
goto use_rma_block;
block = (char *) block + old_size;
/* Round the size up to the nearest page (so that calling
OS_ChangeDynamicArea again later doesn't cause wastage). */
if(xos_read_mem_map_info(&page_size, 0)) goto use_rma_block;
size = ((size + page_size - 1) / page_size) * page_size;
if(xos_change_dynamic_area(buf->dynarea, size, (int *) &size))
goto use_rma_block;
buf->temp_size = size;
}
else {
/* Otherwise allocate a fresh block in the module area. */
use_rma_block:
if(xosmodule_alloc(size, &block) || !block) return 0; /* Fail */
}
#ifdef MemCheck_MEMCHECK
MemCheck_RegisterMiscBlock(block, size);
#endif
return block; /* Success */
}
/* Free a previously-allocated temporary block. */
void replaydriver_temp_free(replaydriver_buffer *buf, void *block)
{
if(buf && buf->dynarea != -1) {
/* Return the dynamic area to its original size. */
xos_change_dynamic_area(buf->dynarea, -buf->temp_size, 0);
buf->temp_size = 0;
}
else {
/* Free the block in the module area. */
if(block) xosmodule_free(block);
}
#ifdef MemCheck_MEMCHECK
MemCheck_UnRegisterMiscBlock(block);
#endif
}
/* Performing a timing check. */
/* Begin a timing check. Requires a pointer to a state variable (used to
store a time). You can do other things in the mean time; when you are
ready, call replaydriver_timecheck_finish(). */
void replaydriver_timecheck_start(replaydriver_driver *driver,
replaydriver_control *control, replaydriver_timecheck_state *state)
{
if(!driver || !control) return;
replaydriver_play_entry(driver, control, 1);
/* Work out the time to wait until. */
*state = os_read_monotonic_time() + SOUND_CHECK_TIME;
if(!*state) *state = 1; /* Once in a blue moon; state must be non-zero. */
}
/* Wait for the timing check to finish. */
void replaydriver_timecheck_finish(replaydriver_driver *driver,
replaydriver_control *control, replaydriver_timecheck_state *state)
{
if(!driver || !control) return;
/* If time checks are necessary, wait for it to finish. */
if(~replaydriver_get_flags(driver) & replaydriver_NO_SOUND_CHECK)
{ while((*state - os_read_monotonic_time()) > 0) { /* Nothing! */ } }
replaydriver_stop(driver, control);
*state = 0;
}
/* End the sound check prematurely (eg. an error may have occurred). */
void replaydriver_timecheck_abort(replaydriver_driver *driver,
replaydriver_control *control, replaydriver_timecheck_state *state)
{
if(!driver || !control) return;
if(*state) {
replaydriver_stop(driver, control);
*state = 0;
}
}
/* Stopping. */
/* Stop playing. Also resets buffers to unfilled. */
void replaydriver_stop(replaydriver_driver *driver,
replaydriver_control *control)
{
if(!driver || !control) return;
/* Tell driver. */
replaydriver_veneer((unsigned) control, 0, (void *) &driver->stop);
/* Set buffers to unfilled. */
((int *) replaydriver_get_buffer(driver, 0))[1] = 1;
((int *) replaydriver_get_buffer(driver, 1))[1] = 1;
}
/* Feeding data. */
/* Feeds the driver more data. Also keeps track of which buffer will become
empty next, so that its status word can be used as a poll word. */
void replaydriver_feed(replaydriver_driver *driver,
replaydriver_control *control, void *data, size_t size)
{
int next_buffer;
if(!driver || !control) return;
/* Which buffer will become empty next? The one that's full now (they
can't both be full). */
if(!*replaydriver_buffer_empty(driver, 0)) next_buffer = 0;
else if(!*replaydriver_buffer_empty(driver, 1)) next_buffer = 1;
else next_buffer = -1; /* Neither are full. */
/* Do the actual buffer filling. */
replaydriver_veneer((unsigned) data, size, (void *) &(driver)->feed);
if(next_buffer == -1) {
/* Try again, which buffer will become empty next? The one that has
just become full (only one must be full). */
if(!*replaydriver_buffer_empty(driver, 0)) next_buffer = 0;
else if(!*replaydriver_buffer_empty(driver, 1)) next_buffer = 1;
}
control->extra.next_buffer = next_buffer;
}